在上一個範例中,是寫死回傳的內容,顯然在現實生活中應該是不會有公司讓你可以這樣做的,而當我們的Controller
開始Reactive了,背後的data store當然也要reactive起來。
一開始的時候其實Spring WebFlux
並不支援關聯式資料庫,只能使用NoSQL
或是Redis
,但還是有許多的業務場景是更適合使用RDB
的,這時候Spring
推出 Reactive Relational Database Connectivity
簡稱R2DBC
,跟JDBC
一樣是DB driver
,但它並不是建立在JDBC
或是其他api之上,因為JDBC
是blocking的api,最後與我們熟悉的Spring Data
結合成為 Spring Data R2DBC
。
build.gralde
多補上兩個dependencies
implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
runtimeOnly 'dev.miku:r2dbc-mysql'
這次開始的範例需要用到mySql,這邊提供docker image(docker hub)的版本,init.sql
須放在與stack.yml
同一層
CREATE DATABASE IF NOT EXISTS test;
USE test;
stack.yml
主要就是有一個可以init的sql執行以及有一個8081的ui介面。
# Use root/example as user/password credentials
version: '3.1'
services:
db:
image: mysql
command: --default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql
restart: always
volumes:
- ./init.sql:/data/application/init.sql
environment:
MYSQL_ROOT_PASSWORD: example
ports:
- 3306:3306
adminer:
image: adminer
restart: always
ports:
- 8081:8080
application.properties
將設定帳號密碼同上面DB設定
spring.r2dbc.url=r2dbc:mysql://localhost:3306/test
spring.r2dbc.username=root
spring.r2dbc.password=example
Greeting.java
Entity
,跟Spring data
相同,有@Id
就會自然被Spring視作為DB物件。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Greeting {
@Id
private Long id;
private String message;
public Greeting(String message) {
this.message = message;
}
}
GreetingRepository
Repository 一樣有很方便的CrudRepository
可以使用,用法基本上是一樣的。
public interface GreetingRepository extends ReactiveCrudRepository<Greeting, Long> {
Mono<Greeting> findById(Long id);
Flux<Greeting> findAll();
Flux<Greeting> findByMessage(String message);
Mono<Void> save(Mono<Greeting> greeting);
}
\src\main\resources\schema.sql
看文件沒有找到會自動產生table,可能還沒支援,不過有提供ini.sql
的方式,先準備schema.sql
在resource目錄下
CREATE TABLE IF NOT EXISTS greeting (id bigint PRIMARY KEY AUTO_INCREMENT, message VARCHAR(255));
在任意地方放上這個initializer
,建議是可以放在命名為DbConfiguration
之類的地方統一管理,這邊測試就直接放在WebFluxGuideApplication
底下
@Bean
ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
initializer.setDatabasePopulator(
new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
return initializer;
}
最後測試一下
@Bean
public CommandLineRunner demo(GreetingRepository repository) {
return (args) -> {
// save a few Greeting
repository
.saveAll(
Arrays.asList(
new Greeting("Hello"),
new Greeting("Hello"),
new Greeting("Yo"),
new Greeting("Nice to meet you"),
new Greeting("Hi")))
.blockLast(Duration.ofSeconds(10));
// fetch all Greeting
log.info("Greeting found with findAll():");
log.info("-------------------------------");
repository
.findAll()
.doOnNext(
greeting -> {
log.info(greeting.toString());
})
.blockLast(Duration.ofSeconds(10));
log.info("");
// fetch an individual Greeting by ID
repository
.findById(1L)
.doOnNext(
greeting -> {
log.info("Greeting found with findById(1L):");
log.info("--------------------------------");
log.info(greeting.toString());
log.info("");
})
.block(Duration.ofSeconds(10));
// fetch Greeting by last name
log.info("Greeting found with findByMessage('Hello'):");
log.info("--------------------------------------------");
repository
.findByMessage("Hello")
.doOnNext(
hello -> {
log.info(hello.toString());
})
.blockLast(Duration.ofSeconds(10));
log.info("");
};
}
今天簡單的將Reactive Programming推進到了DB的世界,下一篇會補充說明。
mybatis 不知道有沒有支援
官方沒有提到,但看起來有人在開發中
https://github.com/linux-china/mybatis-r2dbc